using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Core.LockStep
{
    public interface IFrameDataStream
    {
        byte ReadByte();
        void WriteByte(byte value);

        int ReadInt32();
        void WriteInt32(int value);

        Vector2Int ReadV2I();
        void WriteV2I(Vector2Int value);

        Vector3Int ReadV3I();
        void WriteV3I(Vector3Int value);
    }

    public class FrameDataStream : IFrameDataStream, IPoolObj
    {
        private byte[] buffer;

        private int length;
        public int Length { get { return length; } }

        private int position;
        public int Position { get { return position; } }

        private int capacity;
        private const int DEFAULT_CAPACITY = 64;

        public int bufferMd5;

        public FrameDataStream() : this(DEFAULT_CAPACITY) { }

        public FrameDataStream(int capacity)
        {
            if (capacity <= 0)
                capacity = DEFAULT_CAPACITY;

            this.capacity = capacity;
            buffer = new byte[capacity];
        }

        public void SeekBegin()
        {
            position = 0;
        }

        public void Seek(int position)
        {
            if (position < 0 || position > length)
                throw new ArgumentOutOfRangeException($"位置索引范围异常, position:{position}, length:{length}");

            this.position = position;
        }

        public void SeekEnd()
        {
            position = length;
        }

        public byte[] GetBuffer()
        {
            return buffer;
        }

        public void WriteBuffer(byte[] sourceArray)
        {
            if (buffer.Length < position + sourceArray.Length)
            {
                EnsureCapacity(sourceArray.Length + position);
                Log.Info($"帧数据初始大小不足，需要扩容长度为:{buffer.Length}，应该扩大初始大小以避免GC。");
            }

            Array.Copy(sourceArray, 0, buffer, position, sourceArray.Length);

            position += sourceArray.Length;
            if (position > length)
                length = position;
        }

        public void SetBuffer(Stream sourceStream, int sourceLength)
        {
            if (buffer.Length < sourceLength)
            {
                buffer = new byte[sourceLength];
                Log.Info($"帧数据初始大小不足，需要扩容长度为:{buffer.Length}，应该扩大初始大小以避免GC。");
            }

            sourceStream.Read(buffer, 0, sourceLength);
            length = sourceLength;
        }

        public void Clear()
        {
            length = 0;
            position = 0;
        }

        #region ReadWrite

        public byte ReadByte()
        {
            return buffer[position++];
        }

        public void WriteByte(byte value)
        {
            if (length == buffer.Length)
                EnsureCapacity(buffer.Length + capacity);

            buffer[position++] = value;

            if (position > length)
                length = position;
        }

        private void EnsureCapacity(int length)
        {
            var temp = buffer;
            buffer = new byte[length];
            Array.Copy(temp, buffer, temp.Length);
            Log.Info($"帧数据初始大小不足，需要扩容长度为:{buffer.Length}，应该扩大初始大小以避免GC。");
        }

        public int ReadInt32()
        {
            var value = BitConverter.ToInt32(buffer, position);
            position += 4;
            return value;
        }

        public void WriteInt32(int value)
        {
            WriteByte((byte)value);
            WriteByte((byte)(value >> 8));
            WriteByte((byte)(value >> 16));
            WriteByte((byte)(value >> 24));
        }

        public uint ReadUInt()
        {
            var value = BitConverter.ToUInt32(buffer, position);
            position += 4;
            return value;
        }

        public void WriteUInt(uint value)
        {
            WriteByte((byte)value);
            WriteByte((byte)(value >> 8));
            WriteByte((byte)(value >> 16));
            WriteByte((byte)(value >> 24));
        }

        public Vector2Int ReadV2I()
        {
            return new Vector2Int(ReadInt32(), ReadInt32());
        }

        public void WriteV2I(Vector2Int value)
        {
            WriteInt32(value.x);
            WriteInt32(value.y);
        }

        public Vector3Int ReadV3I()
        {
            return new Vector3Int(ReadInt32(), ReadInt32(), ReadInt32());
        }

        public void WriteV3I(Vector3Int value)
        {
            WriteInt32(value.x);
            WriteInt32(value.y);
            WriteInt32(value.z);
        }

        #endregion
    }
}